﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PServiceBus.Core.Runtime.Extensions;

namespace PServiceBus.Cassandra {
    public sealed class CassandraStore<TObject> : CassandraCollection<TObject> where TObject : class {

        private const string DATA_BUFFER_COLUMN = "Buffer";

        public override IEnumerator<TObject> GetObjects() {
            var client = GetConnection(READ_ONLY_KEY);
            var values = client.ColumnList.Where(x => x.ColumnFamily == CassandraPathInfo.ColumnFamily);
            
            foreach (var value in values) {
                if (value.Data.Any())
                    yield return value.Data.First().Value.Deserialize<TObject>();
            }
        }

        public override bool Add(IEnumerable<TObject> list) {
            var success = false;
            Query(client =>
            {
                if (list.Any()) {
                    foreach (var item in list) {
                        client.Column.InsertOnSubmit(new Cassandraemon.CassandraEntity<Apache.Cassandra.Column>()
                            .SetColumnFamily(CassandraPathInfo.ColumnFamily)
                            .SetKey(GetID(item))
                            .SetData(item.Serialize(), DATA_BUFFER_COLUMN)
                            );
                    }
                }
                success = true;
            });
            return success;
        }

        public override bool Add(TObject obj) {
            var success = false;
            Query(client =>
            {
                client.Column.InsertOnSubmit(new Cassandraemon.CassandraEntity<Apache.Cassandra.Column>()
                            .SetColumnFamily(CassandraPathInfo.ColumnFamily)
                            .SetKey(GetID(obj))
                            .SetData(obj.Serialize(), DATA_BUFFER_COLUMN)
                            );
                success = true;
            });
            return success;
        }

        public override bool Delete(IEnumerable<Func<TObject, bool>> conditions) {
            var objs = this.Where(d => d != null && conditions.Any(c => c(d))).ToList();
            return Delete(objs);
        }

        public override bool Delete(IEnumerable<TObject> list) {
            Query(client =>
            {
                foreach (var obj in list)
                    client.Column.DeleteOnSubmit(x => x.ColumnFamily == CassandraPathInfo.ColumnFamily && x.Key == GetID(obj));
            });
            return true;
        }

        public override bool Delete(Func<TObject, bool> condition) {
            var objs = this.Where(d => d != null && condition(d));
            return Delete(objs);
        }

        public override bool Delete(TObject obj) {
            Query(client =>
            {
                client.Column.DeleteOnSubmit(x => x.ColumnFamily == CassandraPathInfo.ColumnFamily && x.Key == GetID(obj));
            });
            return true;
        }

        public override bool Exists(Func<TObject, bool> condition) {
            return Get(condition) != null;
        }

        public override bool Exists(TObject obj) {
            return Query(client => {
                var list = client.ColumnList.Where(x => x.ColumnFamily == CassandraPathInfo.ColumnFamily && x.Key == GetID(obj));
                return list.First().Data.Any();
            });
        }

        public override TObject Get(Func<TObject, bool> condition) {
            return this.FirstOrDefault(d => d != null && condition(d));
        }

        public override TObject Get(Guid id) {
            return Query(client =>
            {
                var key = id.ToByteArray();
                var list = client.ColumnList.Where(x => x.ColumnFamily == CassandraPathInfo.ColumnFamily && x.Key == key);
                var data = list.First().Data.FirstOrDefault();
                return data != null ? data.Value.Deserialize<TObject>() : null;
            });
        }

        public override bool Refresh(TObject obj) {
            return Add(obj);
        }
    }
}
